#include <slothttpd/stage/send_file_async.hpp>
#include <slothttpd/stage/send_headers.hpp>
#include <slothttpd/server.hpp>
#include <slothttpd/logger.hpp>

namespace slothttpd {
namespace stage {

send_file_async::send_file_async(connection_ptr conn, const char *path, config::file_location &conf) :
    ref_counted(conn),
    buffer_(nullptr),
    buffer_size_(0),
    offset_(0)
{
    (void) conf;
    header_sender_ = c->pool.construct<send_headers>(conn, path, conf,
                                                     boost::bind(&send_file_async::on_headers_sent,
                                                                 send_file_async_ptr(this)));
}

void send_file_async::on_headers_sent(send_file_async_ptr s)
{
    if (s->c->request.method_ == http::method::get) {
        s->responce_file    = s->header_sender_->responce_file;
        s->header_sender_   = nullptr;
        s->buffer_size_     = 64 * 1024;
        s->buffer_          = static_cast<char*>(s->c->pool.allocate_aligned(s->buffer_size_, 512));
        
        s->post_async_read();
    }
}

send_file_async::~send_file_async() noexcept
{
    c->pool.free(buffer_);
}

void send_file_async::post_async_read()
{
    aio::async_read::operation_handler handler = boost::bind(
                &send_file_async::handle_async_read, 
                send_file_async_ptr(this),
                // this order! first error, second bytes_transferred. otherwise won't compile
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred);
    aread.submit(*server::instance->aio, responce_file, buffer_, buffer_size_, offset_, handler);
}

void send_file_async::handle_async_read(send_file_async_ptr r, std::size_t bytes, const boost::system::error_code &err)
{
    if (!err) {
        r->offset_ += bytes;
        
        r->post_send(bytes);
    } else if (err != boost::asio::error::eof) {
        logger() << err.message() << std::endl;
    }
}

void send_file_async::post_send(std::size_t bytes)
{
    boost::asio::async_write(c->socket,
                             boost::asio::buffer(buffer_, bytes),
                             boost::asio::transfer_all(),
                             boost::bind(&send_file_async::handle_send,
                                         send_file_async_ptr(this),
                                         boost::asio::placeholders::error));
}

void send_file_async::handle_send(send_file_async_ptr r, const boost::system::error_code &err)
{
    if (!err) {
        r->post_async_read();
    } else {
        logger() << err.message() << std::endl;
    }
}

}
}
